import 'dart:convert';

import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:jet_rails/jr_app/jr_models/jr_activity_model.dart';
import 'package:jet_rails/jr_app/jr_models/jr_article_model.dart';
import 'package:jet_rails/jr_app/jr_models/jr_user_model.dart';
import 'package:jet_rails/jr_app/jr_models/jr_video_model.dart';
import 'package:jet_rails/jr_app/jr_views/jr_login/jr_login_view.dart';
import 'package:jet_rails/jr_constant/jr_keys.dart';
import 'package:jet_rails/jr_services/jr_apis.dart';
import 'package:jet_rails/jr_services/jr_tracking.dart';
import 'package:jet_rails/jr_utils/jr_device.dart';
import 'package:jet_rails/jr_utils/jr_logger.dart';
import 'package:jet_rails/jr_utils/jr_shared_preferences.dart';
import 'package:jet_rails/jr_utils/jr_toast.dart';

class JrServices extends GetxService {
  static JrServices get instance => Get.find();
  final loginUser = JrLoginUserModel().obs;

  final allActivity = <JrActivityModel>[].obs;
  List<JrActivityModel> get availableActivity => filterBlockedUsersContent(allActivity, (e) => e.author?.userId ?? '');

  final allArticle = <JrArticleModel>[].obs;
  List<JrArticleModel> get availableArticle => filterBlockedUsersContent(allArticle, (e) => e.author?.userId ?? '');

  final allVideo = <JrVideoModel>[].obs;
  List<JrVideoModel> get availableVideo => filterBlockedUsersContent(allVideo, (e) => e.author?.userId ?? '');

  final userList = <JrUserModel>[].obs;
  final blockList = <JrUserModel>[].obs;
  final followedList = <JrUserModel>[].obs;

  String googleKey = '';

  login({String? identityToken}) async {
    int oauthType = identityToken != null ? 3 : 4;
    JrTracking.click(identityToken != null ? JrTrackingPages.pageAppleLogin : JrTrackingPages.pageQuickLogin);
    String token = identityToken ?? JrDevice.deviceId;
    final response = await JrApis.login(oauthType: oauthType, token: token);
    JrLogger.d('login: $response');
    String accessToken = response['token'];
    Map<String, dynamic> loginUserJson = response['userInfo'];
    loginUser.value = JrLoginUserModel.fromJson(loginUserJson);
    JrSP.instance.setString(JrKeys.kAccessToken, accessToken);
    JrSP.instance.setString(JrKeys.kLoginUserInfo, json.encode(loginUserJson));
    return await loginProcess();
  }

  Future<bool> canAutoLogin() async {
    String? token = JrSP.instance.getString(JrKeys.kAccessToken);
    String? string = JrSP.instance.getString(JrKeys.kLoginUserInfo);
    bool logined = JrSP.instance.getBool(JrKeys.kLogined) ?? false;
    return token != null && string != null && logined;
  }

  Future<bool> loginProcess() async {
    try {
      String? token = JrSP.instance.getString(JrKeys.kAccessToken);
      if (loginUser.value.userId == null) {
        String? string = JrSP.instance.getString(JrKeys.kLoginUserInfo);
        Map<String, dynamic> userJson = string != null ? json.decode(string) : {};
        loginUser.value = JrLoginUserModel.fromJson(userJson);
      }
      JrLogger.d('sp userInfo: ${loginUser.toJson()}');
      try {
        final configRes = await JrApis.isValidToken(token: token ?? '');
        JrLogger.d('isValidToken: $configRes');
        JrApis.accessToken = token;
      } catch (e) {
        return false;
      }

      try {
        final configVer = JrSP.instance.getString(JrKeys.kConfigVer) ?? '0';
        var configRes = await JrApis.getAppConfig(configVer);
        JrLogger.d('configRes: $configRes');
        googleKey =
            (configRes['items'] as List).firstWhere((element) => element['name'] == 'google_translation_key')['data'];
        JrSP.instance.setString(JrKeys.kConfigVer, configRes['ver'].toString());
        JrSP.instance.setString(JrKeys.kGoogleKey, googleKey);
      } catch (e) {
        googleKey = JrSP.instance.getString(JrKeys.kGoogleKey) ?? '';
        return false;
      }

      await initData();
      JrSP.instance.setBool(JrKeys.kLogined, true);
      JrTracking.click(JrTrackingPages.pageLoginSuccess);
      return true;
    } catch (e, s) {
      JrLogger.e('loginProcess error $e, $s');
      return false;
    }
  }

  void deleteAccount() async {
    try {
      EasyLoading.show(maskType: EasyLoadingMaskType.black);
      JrTracking.click(JrTrackingPages.pageDeleteAccount);
      JrSP.instance.remove(JrKeys.kFreeTickets);

      await JrApis.deleteAccount();
      logout();
    } catch (e) {
      JrToast.show('Delete account failed');
      JrLogger.e('deleteAccount error $e');
    } finally {
      EasyLoading.dismiss();
    }
  }

  void reset() {
    loginUser.value = JrLoginUserModel();
    allActivity.clear();
    allArticle.clear();
    allVideo.clear();
    userList.clear();
    blockList.clear();
    followedList.clear();
    googleKey = '';
  }

  void logout() async {
    try {
      EasyLoading.show(maskType: EasyLoadingMaskType.black);
      JrTracking.click(JrTrackingPages.pageLogout);
      await JrApis.logout();

      JrSP.instance.remove(JrKeys.kLogined);
      JrSP.instance.remove(JrKeys.kAccessToken);
      JrSP.instance.remove(JrKeys.kLoginUserInfo);
      JrSP.instance.remove(JrKeys.kAllActivity);
      JrSP.instance.remove(JrKeys.kAllArticle);
      JrSP.instance.remove(JrKeys.kAllVideo);
      JrSP.instance.remove(JrKeys.kAllUser);
      JrSP.instance.remove(JrKeys.kAllBlockedUser);
      JrSP.instance.remove(JrKeys.kAllFollowedUser);
      JrSP.instance.remove(JrKeys.kGoogleKey);
      JrSP.instance.remove(JrKeys.kSettingAutoTranslation);
      reset();
      Get.offAll(const JrLoginView());
    } catch (e, s) {
      JrLogger.e('logout error $e  $s');
    } finally {
      EasyLoading.dismiss();
    }
  }

  Future<void> refreshLoginUser() async {
    loginUser.refresh();
    await JrSP.instance.setString(JrKeys.kLoginUserInfo, json.encode(loginUser.toJson()));
  }

  Future<void> initData() async {
    try {
      await initBroadcaster();
      await Future.wait([initActivity(), initArticle(), initVideo()]);
    } catch (e) {
      JrLogger.e('initData $e');
    }
  }

  Future<void> initBroadcaster() async {
    List? allUserJson = JrSP.instance.getStringList(JrKeys.kAllUser);
    List? allBlockedUserJson = JrSP.instance.getStringList(JrKeys.kAllBlockedUser);
    List? allFollowedUserJson = JrSP.instance.getStringList(JrKeys.kAllFollowedUser);
    if (allUserJson != null) {
      for (var item in allUserJson) {
        userList.add(JrUserModel.fromJson(jsonDecode(item)));
      }
    } else {
      final allUsers = await JrApis.broadcasterWallSearch();
      List codeList = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'];
      for (var item in allUsers) {
        JrUserModel userModel = JrUserModel.fromJson(item);
        if (codeList.isNotEmpty) {
          userModel.alias = codeList.removeAt(0);
        }
        userList.add(userModel);
      }
      JrSP.instance.setStringList(JrKeys.kAllUser, userList.map((e) => jsonEncode(e.toJson())).toList());
    }

    if (allBlockedUserJson != null) {
      for (var item in allBlockedUserJson) {
        blockList.add(JrUserModel.fromJson(jsonDecode(item)));
      }
    }

    if (allFollowedUserJson != null) {
      for (var item in allFollowedUserJson) {
        followedList.add(JrUserModel.fromJson(jsonDecode(item)));
      }
    }
  }

  Future<void> initActivity() async {
    List? allActivityJson = JrSP.instance.getStringList(JrKeys.kAllActivity);
    if (allActivityJson != null) {
      for (var item in allActivityJson) {
        allActivity.add(JrActivityModel.fromJson(jsonDecode(item)));
      }
    } else {
      var allActivityConfigJson = await rootBundle.loadString('assets/config/activity_configs.json');
      var activityConfigs = jsonDecode(allActivityConfigJson);
      allActivity.clear();
      for (var item in activityConfigs) {
        allActivity.add(JrActivityModel.fromJson(item));
      }

      for (int i = 0; i < allActivity.length; i++) {
        String? authorCode = allActivity.value[i].authorCode;
        allActivity.value[i].author = userList.firstWhere((user) => user.alias == authorCode);
      }
      saveActivity();
    }
  }

  Future<void> initArticle() async {
    List? allArticleJson = JrSP.instance.getStringList(JrKeys.kAllArticle);

    if (allArticleJson != null) {
      for (var item in allArticleJson) {
        allArticle.add(JrArticleModel.fromJson(jsonDecode(item)));
      }
    } else {
      var articleConfigsJson = await rootBundle.loadString('assets/config/article_configs.json');
      var articleConfigs = jsonDecode(articleConfigsJson);
      allArticle.value.clear();
      for (var item in articleConfigs) {
        allArticle.value.add(JrArticleModel.fromJson(item));
      }

      for (int i = 0; i < allArticle.length; i++) {
        String? authorCode = allArticle.value[i].authorCode;
        allArticle.value[i].author = userList.firstWhere((user) => user.alias == authorCode);
      }
      saveArticle();
    }
  }

  Future<void> initVideo() async {
    List? allVideoJson = JrSP.instance.getStringList(JrKeys.kAllVideo);

    if (allVideoJson != null) {
      for (var item in allVideoJson) {
        allVideo.add(JrVideoModel.fromJson(jsonDecode(item)));
      }
    } else {
      var videoConfigsJson = await rootBundle.loadString('assets/config/video_configs.json');
      var videoConfigs = jsonDecode(videoConfigsJson);
      final mediaModels = await JrApis.mediaSearch(videoConfigs);
      allVideo.value.clear();
      for (var item in mediaModels) {
        allVideo.value.add(JrVideoModel.fromJson(item));
      }

      for (int i = 0; i < allVideo.length; i++) {
        allVideo.value[i].author = userList[i % userList.length];
        allVideo.value[i].title = videoConfigs[i % videoConfigs.length]['title'];
      }
      saveVideo();
    }
  }

  Future<bool> saveActivity() async {
    return JrSP.instance
        .setStringList(JrKeys.kAllActivity, allActivity.value.map((e) => jsonEncode(e.toJson())).toList());
  }

  Future<bool> saveArticle() async {
    return JrSP.instance
        .setStringList(JrKeys.kAllArticle, allArticle.value.map((e) => jsonEncode(e.toJson())).toList());
  }

  Future<bool> saveVideo() async {
    return JrSP.instance.setStringList(JrKeys.kAllVideo, allVideo.value.map((e) => jsonEncode(e.toJson())).toList());
  }

  bool isMe(String userId) => loginUser.value.userId == userId;
  bool isFollowed(String userId) => followedList.value.any((e) => e.getId == userId);
  bool isBlockUser(String userId) => blockList.value.any((e) => e.getId == userId);

  Future<void> report(String userId, String reportComplain) async {
    try {
      JrApis.reportOrBlockUser(userId, reportComplain: reportComplain);
    } catch (e) {
      JrLogger.e('eeeeeeee $e');
    }
  }

  Future<void> refreshFollowedList() async {
    try {
      final res = await JrApis.getFollowedList();
      followedList.value.clear();
      for (var item in res) {
        JrUserModel user = JrUserModel.fromJson(item);
        followedList.value.add(user);
      }
      followedList.refresh();
      JrSP.instance
          .setStringList(JrKeys.kAllFollowedUser, followedList.value.map((e) => jsonEncode(e.toJson())).toList());
    } catch (e) {
      JrLogger.e('refreshFollowedList error $e');
    }
  }

  Future unFollow(String userId) async {
    EasyLoading.show();
    try {
      await JrApis.unfollowUser(userId);
      await refreshFollowedList();
    } finally {
      EasyLoading.dismiss();
    }
  }

  Future<void> follow(String userId) async {
    try {
      await JrApis.followUser(userId);
      await refreshFollowedList();
    } catch (e) {
      JrLogger.e('follow error $e');
    }
  }

  Future<void> refreshBlockList() async {
    try {
      var res = await JrApis.getBlockedUsers();
      blockList.value.clear();
      for (var item in res) {
        JrUserModel model = JrUserModel.fromJson(item);
        model.nickname = item['nickName'];
        model.userId = item['broadcasterId'];
        blockList.value.add(model);
      }
      blockList.refresh();
      JrSP.instance
          .setStringList(JrKeys.kAllBlockedUser, blockList.value.map((e) => jsonEncode(e.toJson())).toList());
    } catch (e) {
      JrLogger.e('refreshBlockList error $e');
    }
  }

  Future<void> blockUser(String userId) async {
    try {
      await unFollow(userId);
      await JrApis.reportOrBlockUser(userId);
      await refreshBlockList();
    } catch (e) {
      JrLogger.e('blockUser error $e');
    }
  }

  Future<void> unblockUser(String userId) async {
    try {
      await JrApis.unblockUser(userId);
      await refreshBlockList();
    } catch (e) {
      JrLogger.e('unblockUser error $e');
    }
  }

  List<T> filterBlockedUsersContent<T>(List<T> all, String Function(T) userIdGetter) {
    final Set<String> blockedUserIds = blockList.map((e) => e.userId).toSet();
    return all.where((e) => !blockedUserIds.contains(userIdGetter(e))).toList();
  }
}
